# -*- coding: ISO-8859-1 -*-
"""\
Module for manipulating bibliographic data in the BibTeX format

This module is a basic interface to the BibTeX format. 

"""

# Copyright (c) 2006 Kjell Magne Fauske
# Redistribution and use in source and binary forms, with or without 
# modification, are permitted.


#import base
import re
import biblibutils
import textwrap
from sets import Set


# Define the various entry types

general_fields = ['abstract','keywords', 'note', 
                  'url', 'doi', 'language', 'ISSN']

article_entry = {
    'required' : ['title', 'author', 'journal', 'year'],
    'optional' : ['volume','number', 'pages', 'month','publisher'] + general_fields
}

misc_entry = {
    'required' : [],
    'optional' : [],
}

inproceedings_entry = {
    'required' : ['title','author','booktitle','year'],
    'optional' : ['volume','number','pages','address','month'] + general_fields
}

entrymap = {
    'inproceedings' : inproceedings_entry,
    'article' : article_entry,
}


wrapper = textwrap.TextWrapper(initial_indent="",subsequent_indent='       ')

class BibEntry(dict):
    """A bibliographic record.

    BibEntry is a very simple attempt to implement an ordered dictionary.
    Use the append method to use this feature. The current implementation is
    very basic and should be replaced with a better one. 
    """
    # Todo: User a better ordered dictionary implementation.
    type = ""
    bibkey = ""
    _order = []
    def __init__(self):
        self._order = []
    def fromString(self, src):
        """Parse src and return an BibEntry instance"""
        pass
    def toString(self, entry=None):
        """Return BibEntry as string"""
        pass
    def items(self):
        if len(self._order) > 0:
            return [(key, self[key]) for key in self._order]
        else: 
            return dict.items(self)
    def append(self, key, value):
        self[key] = value
        self._order.append(key)
    def __delitem__(self,key):
        try:
            self._order.remove(key)
        except:
            pass
        dict.__delitem__(self,key)
    def __setitem__(self, key, value):
        dict.__setitem__(self,key,value)
        #self._order.append(key) # bug   
        
class BibTeXEntry(BibEntry):
    def __init__(self):
        BibEntry.__init__(self)
        self.bibkey = ''
        self.type = ''
    def generateKey(self):
        """Generate a bibtex key.
        
        The key is simply the first authors last name and the year of 
        publications. 
        """
        # TODO: Move generateKey to BibTeXFormatter?
        # TODO: Add some options for the bibtex key generation.
        authors = self.get('author',[''])[0]
        if authors:
            firstauthor = biblibutils.normalizeName(authors).split(',')[0]
            year = self.get('year','')
            self.bibkey = "%s%s" % (firstauthor, year)
            
    def fromString(self, src):
        """Parse src and return an BibTeXEntry instance"""
        pass
    def toString(self, entry=None):
        """Return BibTexEntry as string"""
        # toString can be used both as a metod and a function
        if entry == None:
            entry = self
        # output entry type and key    
        s = "@%s{%s,\n" % (self.type.upper(), self.bibkey)
        # iterate over each item in entry
        for (key, value) in entry.items():
            # if value is a list, combine list items to a single string
            if type(value) == list:
                if key == 'author' :
                    value = " and ".join(value)
                else :
                    value =", ".join(value)  
            # output key, value pair  
            if value: 
                if value[0] == '@':
                    # string macro
                    s += "  %s = %s,\n" % (key, value[1:]) 
                else:
                    s += "  %s = {%s},\n" % (key, value) 
        # end entry
        s += "}\n"
        return s    
        
class BibTeXFormatter:
    """
    A class for handling the formatting of BibTeX records
    """
    # TODO: Formatter options should be documented somewhere
    def __init__(self, bibtexentry, options= {}):
        """Initialize the formatter.
        
        Input:
          bibtexentry - A BibTeXEntry instance to format
          options - A dictionary with format options
            - generatebibkey
            - prettyprint
            - ieeebst
            - firstnamefirst
            - braceuppercase
            - 
        """
        self.options = options
        self.bibtexentry = bibtexentry
        pass
    def format(self):
        """Format the BibTeXEntry instance submitted in init"""
        entry = self.bibtexentry
        options = self.options
        if options.get('generatebibkey',None):
            entry.generateKey()
        
        if options.get('prettyprint',None):
            abstract = entry.get('abstract',None)
            if abstract:
                entry['abstract'] = wrapper.fill(abstract)
        
        if options.get('ieeebst',False):
            journal = entry.get('journal','')
            # replace with string macro
            import ieeebstdata
            journal_macro = ieeebstdata.ieeejournals.get(journal, None)
            if journal_macro:
                entry['journal'] = '@'+journal_macro
        if options.get('firstnamefirst'):
            authors = entry.get('author','')
            if authors:
                tmplist = []
                for author in authors:
                    tmplist.append(biblibutils.formatFirstnameFirst(author))
                entry['author'] = tmplist
        # It is important that the braces option is checked after all
        # string macro generation.
        if options.get('braceuppercase', None):
            title = entry.get('title',None)
            if title:
                entry['title'] = biblibutils.embraceUppercase(title)
            booktitle = entry.get('booktitle',None)
            if booktitle:
                entry['booktitle'] = biblibutils.embraceUppercase(booktitle)
            journal = entry.get('journal',None)
            if journal:
                entry['journal'] = biblibutils.embraceUppercase(journal)
        if options.get('braceuppercaseletters', None):
            title = entry.get('title',None)
            if title:
                entry['title'] = biblibutils.embraceUppercaseLetters(title)
            booktitle = entry.get('booktitle',None)
            if booktitle:
                entry['booktitle'] = biblibutils.embraceUppercaseLetters(booktitle)
            journal = entry.get('journal',None)
            if journal:
                entry['journal'] = biblibutils.embraceUppercaseLetters(journal)
                
        for (key, value) in entry.items():
            # if value is a list, combine list items to a single string
            if type(value) == list:
                if key == 'author' :
                    value = " and ".join(value)
                else :
                    value =", ".join(value)  
            # output key, value pair  
            if value: 
                if value[0] != '@':
                    # skip string macros
                    entry[key] = biblibutils.escapeTeXChars(value)
        if options.get('prettyprint',None):
            keywords = entry.get('keywords',None)
            if keywords:
            	if len(keywords) > 4:
                	entry['keywords'] = wrapper.fill(keywords)
                
               
        

if __name__ == '__main__':
    en = BibTeXEntry()
    fo = BibTeXFormatter(en, {'generatebibkey':True})
    en.type = 'article'
    en['author'] = ['Kjell Magne Fauske', 'Per Spelmann']
    en['keywords'] = ['GPS', 'INS', 'Navigation']
    en['year'] = '2005'
    